// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
 ******************************************************************************
 * Copyright (C) 2007-2009, International Business Machines Corporation and   *
 * others. All Rights Reserved.                                               *
 ******************************************************************************
 */

package com.ibm.icu.impl.duration;

import java.util.Locale;
import java.util.TimeZone;

/**
 * Abstract factory object used to create DurationFormatters. DurationFormatters are immutable once
 * created.
 *
 * <p>Setters on the factory mutate the factory and return it, for chaining.
 *
 * <p>Subclasses override getFormatter to return a custom DurationFormatter.
 */
class BasicDurationFormatterFactory implements DurationFormatterFactory {
    private BasicPeriodFormatterService ps;
    private PeriodFormatter formatter;
    private PeriodBuilder builder;
    private DateFormatter fallback;
    private long fallbackLimit;
    private String localeName;
    private TimeZone timeZone;
    private BasicDurationFormatter f; // cache

    /** Create a default formatter for the current locale and time zone. */
    BasicDurationFormatterFactory(BasicPeriodFormatterService ps) {
        this.ps = ps;
        this.localeName = Locale.getDefault().toString();
        this.timeZone = TimeZone.getDefault();
    }

    /**
     * Set the period formatter used by the factory. New formatters created with this factory will
     * use the given period formatter.
     *
     * @return this BasicDurationFormatterFactory
     */
    @Override
    public DurationFormatterFactory setPeriodFormatter(PeriodFormatter formatter) {
        if (formatter != this.formatter) {
            this.formatter = formatter;
            reset();
        }
        return this;
    }

    /**
     * Set the builder used by the factory. New formatters created with this factory will use the
     * given locale.
     *
     * @param builder the builder to use
     * @return this BasicDurationFormatterFactory
     */
    @Override
    public DurationFormatterFactory setPeriodBuilder(PeriodBuilder builder) {
        if (builder != this.builder) {
            this.builder = builder;
            reset();
        }
        return this;
    }

    /**
     * Set a fallback formatter for durations over a given limit.
     *
     * @param fallback the fallback formatter to use, or null
     * @return this BasicDurationFormatterFactory
     */
    @Override
    public DurationFormatterFactory setFallback(DateFormatter fallback) {
        boolean doReset =
                fallback == null ? this.fallback != null : !fallback.equals(this.fallback);
        if (doReset) {
            this.fallback = fallback;
            reset();
        }
        return this;
    }

    /**
     * Set a fallback limit for durations over a given limit.
     *
     * @param fallbackLimit the fallback limit to use, or 0 if none is desired.
     * @return this BasicDurationFormatterFactory
     */
    @Override
    public DurationFormatterFactory setFallbackLimit(long fallbackLimit) {
        if (fallbackLimit < 0) {
            fallbackLimit = 0;
        }
        if (fallbackLimit != this.fallbackLimit) {
            this.fallbackLimit = fallbackLimit;
            reset();
        }
        return this;
    }

    /**
     * Set the name of the locale that will be used when creating new formatters.
     *
     * @param localeName the name of the Locale
     * @return this BasicDurationFormatterFactory
     */
    @Override
    public DurationFormatterFactory setLocale(String localeName) {
        if (!localeName.equals(this.localeName)) {
            this.localeName = localeName;
            if (builder != null) {
                builder = builder.withLocale(localeName);
            }
            if (formatter != null) {
                formatter = formatter.withLocale(localeName);
            }
            reset();
        }
        return this;
    }

    /**
     * Set the name of the locale that will be used when creating new formatters.
     *
     * @param timeZone The time zone to use.
     * @return this BasicDurationFormatterFactory
     */
    @Override
    public DurationFormatterFactory setTimeZone(TimeZone timeZone) {
        if (!timeZone.equals(this.timeZone)) {
            this.timeZone = timeZone;
            if (builder != null) {
                builder = builder.withTimeZone(timeZone);
            }
            reset();
        }
        return this;
    }

    /**
     * Return a formatter based on this factory's current settings.
     *
     * @return a BasicDurationFormatter
     */
    @Override
    public DurationFormatter getFormatter() {
        if (f == null) {
            if (fallback != null) {
                fallback = fallback.withLocale(localeName).withTimeZone(timeZone);
            }
            formatter = getPeriodFormatter();
            builder = getPeriodBuilder();

            f = createFormatter();
        }
        return f;
    }

    /**
     * Return the current period formatter.
     *
     * @return the current period formatter
     */
    public PeriodFormatter getPeriodFormatter() {
        if (formatter == null) {
            formatter = ps.newPeriodFormatterFactory().setLocale(localeName).getFormatter();
        }
        return formatter;
    }

    /**
     * Return the current builder.
     *
     * @return the current builder
     */
    public PeriodBuilder getPeriodBuilder() {
        if (builder == null) {
            builder =
                    ps.newPeriodBuilderFactory()
                            .setLocale(localeName)
                            .setTimeZone(timeZone)
                            .getSingleUnitBuilder();
        }
        return builder;
    }

    /**
     * Return the current fallback formatter.
     *
     * @return the fallback formatter, or null if there is no fallback formatter
     */
    public DateFormatter getFallback() {
        return fallback;
    }

    /**
     * Return the current fallback formatter limit
     *
     * @return the limit, or 0 if there is no fallback.
     */
    public long getFallbackLimit() {
        return fallback == null ? 0 : fallbackLimit;
    }

    /**
     * Return the current locale name.
     *
     * @return the current locale name
     */
    public String getLocaleName() {
        return localeName;
    }

    /**
     * Return the current locale name.
     *
     * @return the current locale name
     */
    public TimeZone getTimeZone() {
        return timeZone;
    }

    /** Create the formatter. All local fields are already initialized. */
    protected BasicDurationFormatter createFormatter() {
        return new BasicDurationFormatter(
                formatter, builder, fallback, fallbackLimit, localeName, timeZone);
    }

    /**
     * Clear the cached formatter. Subclasses must call this if their state has changed. This is
     * automatically invoked by setBuilder, setFormatter, setFallback, setLocaleName, and
     * setTimeZone
     */
    protected void reset() {
        f = null;
    }
}
